/* ======================================================================== */
/* NAME                                                                     */
/*  chroma_resampling.c 										            */
/*                                                                          */
/*	DESCRIPTION																*/
/* This file contains chroma resampling routines for use with the ZENO      */
/* Video Port.                                                              */
/* All these routines have been written using the DMA streaming (dstr)      */
/* interface. Specifically, it contains the following APIs:                 */
/* YUV422to420v() - This routine performs the vertical part of chroma       */
/*                  resampling in going from 4:2:2 to 4:2:0                 */
/* YUV420to422v() - This routine performs the vertical part of chroma       */
/*                  resampling in going from 4:2:0 to 4:2:2                 */
/* ------------------------------------------------------------------------ */
/*            Copyright (c) 2002 Texas Instruments, Incorporated.           */
/*                           All Rights Reserved.                           */
/* ======================================================================== */

/* ======================================================================== */
/* basic header includes                                                    */
/* ======================================================================== */
#include <stdio.h>
#include <stdlib.h>
#include <std.h>

/* ======================================================================== */
/* CSL includes                                                             */
/* ======================================================================== */
#include <csl.h>
#include <csl_irq.h>
#include <csl_dat.h>
#include <csl_cache.h>
#include <csl_legacy.h>

/* ======================================================================== */
/* include files for DMA streaming and scaling kernel that performs         */
/* vertical chroma resampling                                               */
/* ======================================================================== */
#include "dstr_2d.h"
#include "scale_v2_h.h"

/* ======================================================================== */
/* Image and scratch buffer structures for pre-post processing operations   */
/* ======================================================================== */

typedef struct {
					unsigned char *Y_data;
					unsigned char *Cb_data;
					unsigned char *Cr_data;
				} IMG;
typedef struct {
					unsigned char *in_data;
					int  size;
				}SCRATCH;			
					



/* ======================================================================== */
/* YUV422to420v() - vertical chroma resampling of source                    */
/* INTERFACE:                                                               */
/* void YUV422to420v(                                                       */
/*                  void *in,                                               */
/*                  void *out,                                              */
/*                  int width,                                              */
/*                  int height,                                             */
/*                  void *scratch                                           */
/*                  );                                                      */
/* RETURN VALUE: None                                                       */
/* PARAMETERS:                                                              */
/* in  - pointer to input data(planar Y:Cb:Cr 4:2:2 format)                 */
/* out - pointer to output data (planar Y:Cb:Cr 4:2:0 format)               */
/* width - pixels per line of source luma                                   */
/* height - number of lines of source luma                                  */
/* scratch - pointer to scratch buffer in DSP's internal memory(used by     */
/*           dstr interface for DMA operation)                              */
/* ======================================================================== */

void YUV422to420v(void *in,void *out,int width,int height,void *scratch) {
	
	int i,err_code;
	unsigned int id;
	
	/* ==================================================================== */
	/* External Buffer pointers                                             */
	/* ==================================================================== */
	unsigned char *Cr_in,*Cb_in;
	unsigned char *Cr_out,*Cb_out;
	
	/* ==================================================================== */
	/* Internal scratch pointer                                             */
	/* ==================================================================== */
	unsigned char *int_mem;
	
	/* ==================================================================== */
	/* dma stream objects                                                   */
	/* ==================================================================== */
	dstr_t din_Cr,din_Cb,dout_Cr,dout_Cb;
	
	
	/* ==================================================================== */
	/* Internal Buffer pointers                                             */
	/* ==================================================================== */
	unsigned char *Cb_in_int,*Cr_in_int,*Cb_out_int,*Cr_out_int;	
	unsigned char *Cb_in_sc,*Cr_in_sc,*Cb_out_sc,*Cr_out_sc;
	
	
	/* ==================================================================== */
	/* get pointers to input and output data(in external memory)            */
	/* ==================================================================== */
	Cr_in = ((IMG *)in)->Cr_data;
	Cb_in = ((IMG *)in)->Cb_data;
	
	Cr_out = ((IMG *)out)->Cr_data;
	Cb_out = ((IMG *)out)->Cb_data;	
	
	/* ==================================================================== */
	/* setup the internal scratch(work) buffers                             */
	/* ==================================================================== */
	int_mem = ((SCRATCH *)scratch)->in_data;
	Cb_in_sc  = int_mem;
	Cr_in_sc  = int_mem + 4  * width;
	Cb_out_sc = int_mem + 8  * width;
	Cr_out_sc = int_mem + 10  * width;
	
	/* ==================================================================== */
	/* The luminance data is not processed at all                           */
	/* Check if the user passed the same buffer pointers for input and      */
	/* output Y, else copy the data from input to output buffer             */
	/* ==================================================================== */
	
	if(((IMG *)in)->Y_data != ((IMG *)out)->Y_data){
	    for(i=0;i<height;i++){
	        id = DAT_copy(((IMG *)in)->Y_data +i*width,int_mem,width);	
	        DAT_wait(id);	    
	        id = DAT_copy(int_mem,((IMG *)out)->Y_data +i*width,width);	
	    }
	}
	
	DAT_wait(id);
	                     
	
	/* ==================================================================== */
	/* Set up the DMA streams                                               */
	/* ==================================================================== */
	
	
	/* ==================================================================== */
	/* open input chroma streams with the following parameters:             */
	/* size of external buffer -->  (width/2)*height (4:2:2 data)           */
	/* size of internal buffer -->  width*4 (work buffer needs to be atleast*/
	/*                              width*2 but we overallocate here)       */	
	/* size of each get        -->  width ( bring in two lines of chroma,   */	
	/*                               each width/2 pixels)                   */ 
	/* number of gets          -->  1                                       */
	/* external pointer stride -->  width   (stride by an amount equal to   */
	/*                              size of each get)                       */
	/* window size             -->  1 (double buffering)                    */	
	/* ==================================================================== */
	err_code = dstr_open(&din_Cb,
						 Cb_in,
						 width * height>> 1,
						 Cb_in_sc,
					 	 width * 4,
						 width,
					 	 1,
					  	 width,
						 1,
					  	 DSTR_INPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif
    err_code = dstr_open(&din_Cr,
						 Cr_in,
						 width * height>> 1,
						 Cr_in_sc,
					 	 width * 4,
						 width,
					 	 1,
					  	 width,
						 1,
					  	 DSTR_INPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif		
    
	/* ==================================================================== */
	/* open output chroma streams with the following parameters:            */
	/* size of external buffer -->  (width/2)*(height/2) (4:2:0 data)       */
	/* size of internal buffer -->  width (work buffer needs to be atleast  */
	/*                              twice the size of actual data sent out) */	
	/* size of each put        -->  width/2 ( send out one line of chroma,  */	
	/*                               of width/2 pixels)                     */ 
	/* number of puts          -->  1                                       */
	/* external pointer stride -->  width/2  (stride by an amount equal to  */
	/*                              size of each put)                       */
	/* window size             -->  1 (double buffering)                    */	
	/* ==================================================================== */
    err_code = dstr_open(&dout_Cb,
						 Cb_out,
						 width * height >> 2,
						 Cb_out_sc,
					 	 width ,
						 width >> 1,
					 	 1,
					  	 width >> 1,
						 1,
					  	 DSTR_OUTPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif
    
    err_code = dstr_open(&dout_Cr,
						 Cr_out,
						 width * height >> 2,
						 Cr_out_sc,
					 	 width,
						 width >> 1,
					 	 1,
					  	 width >> 1,
						 1,
					  	 DSTR_OUTPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif
    
    
	/* ==================================================================== */
	/* Data processing loop:                                                */
	/*                                                                      */
	/* Bring in data from external memory                                   */
	/*                                                                      */
	/* Use vertical half scaling kernel on chroma data in internal memory   */
	/*                                                                      */
	/* Send data back to external memory                                    */	
	/* ==================================================================== */

	for(i=0; i<height >> 1; i++) {
			
		Cb_in_int  = (unsigned char *)dstr_get(&din_Cb);
		Cr_in_int  = (unsigned char *)dstr_get(&din_Cr);
		
		Cb_out_int = (unsigned char *)dstr_put(&dout_Cb);
		Cr_out_int = (unsigned char *)dstr_put(&dout_Cr);
		
		scale_v2_cn(Cb_in_int,width>>1,width>>1,2,Cb_out_int);
		scale_v2_cn(Cr_in_int,width>>1,width>>1,2,Cr_out_int);
		
		
	}
	
	/* ==================================================================== */
	/* DMA out the last lines                                               */
	/* ==================================================================== */
    dstr_put(&dout_Cb);
	dstr_put(&dout_Cr);
	
	/* ==================================================================== */
	/* close the DMA streams                                                */
	/* ==================================================================== */
	dstr_close(&din_Cb);
	dstr_close(&din_Cr);
	dstr_close(&dout_Cb);
	dstr_close(&dout_Cr);
	
}


/* ======================================================================== */
/* YUV420to422v() - vertical chroma resampling of source                    */
/* INTERFACE:                                                               */
/* void YUV420to422v(                                                       */
/*                  void *in,                                               */
/*                  void *out,                                              */
/*                  int width,                                              */
/*                  int height,                                             */
/*                  void *scratch                                           */
/*                  );                                                      */
/* RETURN VALUE: None                                                       */
/* PARAMETERS:                                                              */
/* in  - pointer to input data(planar Y:Cb:Cr 4:2:0 format)                 */
/* out - pointer to output data (planar Y:Cb:Cr 4:2:2 format)               */
/* width - pixels per line of source luma                                   */
/* height - number of lines of source luma                                  */
/* scratch - pointer to scratch buffer in DSP's internal memory(used by     */
/*           dstr interface for DMA operation)                              */
/* ======================================================================== */
void YUV420to422v(void *in,void *out,int width,int height,void *scratch) {
	
	unsigned int id;
	int i,err_code;
	
	/* ==================================================================== */
	/* External Buffer pointers                                             */
	/* ==================================================================== */
	unsigned char *Cr_in,*Cb_in;
	unsigned char *Cr_out,*Cb_out;
	
	/* ==================================================================== */
	/* Internal scratch pointer                                             */
	/* ==================================================================== */
	unsigned char *int_mem;
	
	/* ==================================================================== */
	/* dma stream objects                                                   */
	/* ==================================================================== */
	dstr_t din_Cr,din_Cb,dout_Cr,dout_Cb;
	
	/* ==================================================================== */
	/* Internal Buffer/scratch pointers                                     */
	/* ==================================================================== */
	unsigned char *Cb_in_int,*Cr_in_int,*Cb_out_int,*Cr_out_int;		
	unsigned char *Cb_in_sc,*Cr_in_sc,*Cb_out_sc,*Cr_out_sc;	
	
	/* ==================================================================== */
    /* get pointers to input and output data(in external memory)            */
    /* ==================================================================== */
	
	Cr_in = ((IMG *)in)->Cr_data;
	Cb_in = ((IMG *)in)->Cb_data;
	
	
	// fixed the bug : color mismatch  
	Cb_out = ((IMG *)out)->Cr_data;
	Cr_out = ((IMG *)out)->Cb_data;	
	
	/* ==================================================================== */
	/* The luminance data is not processed at all                           */
	/* Check if the user passed the same buffer pointers for input and      */
	/* output Y, else copy the data from input to output buffer             */
	/* ==================================================================== */	
	
	if(((IMG *)in)->Y_data != ((IMG *)out)->Y_data){
	     for(i=0;i<height;i++){
	        id = DAT_copy(((IMG *)in)->Y_data +i*width,int_mem,width);	
	        DAT_wait(id);	    
	        id = DAT_copy(int_mem,((IMG *)out)->Y_data +i*width,width);	
	     }
	}
	
	DAT_wait(id);	
	
	
	/* ==================================================================== */
    /* setup the internal scratch buffer                                    */
    /* ==================================================================== */
	
	int_mem = ((SCRATCH *)scratch)->in_data;
	Cb_in_sc  = int_mem;
	Cr_in_sc  = int_mem + 2  * width;
	Cb_out_sc = int_mem + 4  * width;
	Cr_out_sc = int_mem + 8 * width;

    /* ==================================================================== */
	/* Set up the DMA streams                                               */
	/* ==================================================================== */
	
	
	/* ==================================================================== */
	/* open input chroma streams with the following parameters:             */
	/* size of external buffer -->  (width/2)*(height/2) (4:2:0 data)       */
	/* size of internal buffer -->  width (twice the actual length of data  */
	/*                              brought in each time)                   */
	/* size of each get        -->  width/2   (bring in one line of chroma) */
	/* number of gets          -->  1                                       */
	/* external pointer stride -->  width/2   (stride by an amount equal to */
	/*                              size of each get)                       */
	/* window size             -->  1 (double buffering)                    */	
	/* ==================================================================== */
	err_code = dstr_open(&din_Cb,
						 Cb_in,
						 width * height >> 2,
						 Cb_in_sc,
					 	 width,
						 width>>1,
					 	 1,
					  	 width>>1,
						 1,
					  	 DSTR_INPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif
    err_code = dstr_open(&din_Cr,
						 Cr_in,
						 width * height >> 2,
						 Cr_in_sc,
					 	 width,
						 width>>1,
					 	 1,
					  	 width>>1,
						 1,
					  	 DSTR_INPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif		
    
	/* ==================================================================== */
	/* open output chroma streams with the following parameters:            */
	/* size of external buffer -->  (width/2)*(height) (4:2:2 data)         */
	/* size of internal buffer -->  width*2 (twice the actual length of data*/
	/*                              sent out  each time)                    */
	/* size of each put        -->  width   (send out two lines of chroma   */
	/*                              each time of width/2 pixels)            */
	/* number of puts          -->  1                                       */
	/* external pointer stride -->  width   (stride by an amount equal to   */
	/*                              size of each put)                       */
	/* window size             -->  1 (double buffering)                    */	
	/* ==================================================================== */
    err_code = dstr_open(&dout_Cb,
						 Cb_out,
						 width * height >> 1,
						 Cb_out_sc,
					 	 width * 2,
						 width,
					 	 1,
					  	 width,
						 1,
					  	 DSTR_OUTPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif
    
    err_code = dstr_open(&dout_Cr,
						 Cr_out,
						 width * height >> 1,
						 Cr_out_sc,
					 	 width * 2 ,
						 width,
					 	 1,
					  	 width,
						 1,
					  	 DSTR_OUTPUT);
	#ifdef DEBUG                     
    if(err_code)
    {
        LOG_printf(&trace, "error opening Cb buffer:%d \n", 
                   err_code);
    }
    #endif


    /* ==================================================================== */
	/* Data processing loop:                                                */
	/*                                                                      */
	/* Bring in data from external memory                                   */
	/*                                                                      */
	/* Use memcpy on internal memory buffers to double the chroma lines     */
	/*                                                                      */
	/* Send data back to external memory                                    */	
	/* ==================================================================== */
	
	for(i=0; i<height >> 1; i++) {	
		
		Cb_in_int  = (unsigned char *)dstr_get(&din_Cb);
		Cr_in_int  = (unsigned char *)dstr_get(&din_Cr);
		
		Cb_out_int = (unsigned char *)dstr_put(&dout_Cb);
		Cr_out_int = (unsigned char *)dstr_put(&dout_Cr);
		
		memcpy(Cb_out_int,Cb_in_int,width>>1);
		memcpy(Cb_out_int+(width>>1),Cb_in_int,width>>1);
		memcpy(Cr_out_int,Cr_in_int,width>>1);
		memcpy(Cr_out_int+(width>>1),Cr_in_int,width>>1);
		
	}
		
		
	/* ==================================================================== */
	/* DMA out the last lines                                               */
	/* ==================================================================== */	
    dstr_put(&dout_Cb);
	dstr_put(&dout_Cr);
	
	/* ==================================================================== */
	/* close the DMA streams                                                */
	/* ==================================================================== */
	dstr_close(&din_Cb);
	dstr_close(&din_Cr);
	dstr_close(&dout_Cb);
	dstr_close(&dout_Cr);
}


